﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace Jemgine.Engine
{
	[Category("Physics"), Component]
	public class ActorPhysics : Physics
	{
		public Vector2 Dimensions { get; set; }
		public bool PreventRotation { get; set; }

        [ContentSerializerIgnore, System.ComponentModel.Browsable(false)]
        public Jemgine.Math.OBB BoundingBox
        {
            get
            {
                var R = new Jemgine.Math.OBB(PhysicsBody.Position,
                    Vector2.Transform(Dimensions / 2, Matrix.CreateRotationZ(PhysicsBody.GetAngle())));
                return R;
            }
        }

        [ContentSerializerIgnore, System.ComponentModel.Browsable(false)]
        public Vector2 FacingVector
        {
            get
            {
                var R = Vector2.UnitX;
                if (Me.Facing == Data.Facings.Left) R.X = -1.0f;
                return Vector2.Transform(R, Matrix.CreateRotationZ(PhysicsBody.GetAngle()));
            }
        }        

		[Microsoft.Xna.Framework.Content.ContentSerializerIgnore
#if (!XBOX)
		,System.ComponentModel.Browsable(false)
#endif
		]
		public bool IsOnGround { get; set; }

        [Microsoft.Xna.Framework.Content.ContentSerializerIgnore
#if (!XBOX)
, System.ComponentModel.Browsable(false)
#endif
]
        public int TimeSinceOnGround { get; set; }

        [Microsoft.Xna.Framework.Content.ContentSerializerIgnore
#if (!XBOX)
, System.ComponentModel.Browsable(false)
#endif
]
        public Vector2 GroundNormal { get; set; }

        public ActorPhysics()
        {
            AddOutput("OnCrushed", null);

            Mass = 8;
        }

        public override void _Initialize(Microsoft.Xna.Framework.Content.ContentManager Content)
		{
			IsOnGround = false;

			PhysicsModule Module = Me.Sim.GetModule<PhysicsModule>();
            Module.Actors.Add(this);

			Box2D.XNA.BodyDef BodyDef = new Box2D.XNA.BodyDef();
			Box2D.XNA.PolygonShape Shape = new Box2D.XNA.PolygonShape();
            Box2D.XNA.CircleShape FootShape = new Box2D.XNA.CircleShape();
			Box2D.XNA.FixtureDef FixtureDef = new Box2D.XNA.FixtureDef();
			Box2D.XNA.MassData MassData = new Box2D.XNA.MassData();

			BodyDef.position = Me.Position;
			BodyDef.type = Box2D.XNA.BodyType.Dynamic;
			BodyDef.allowSleep = AllowSleep;
			BodyDef.bullet = IsBullet;
			//BodyDef.AffectedByGravity = AffectedByGravity;
			BodyDef.linearDamping = LinearDamping;
			//if (PreventRotation) BodyDef.fixedRotation = true;
			_physicsBody = Module.PhysicsSimulator.CreateBody(BodyDef);
            _physicsBody.Gravity = (AffectedByGravity ? Gravity : Vector2.Zero);

			Shape.SetAsBox(Dimensions.X / 2, (Dimensions.Y - (Dimensions.X/2)) / 2, new Vector2(0, Dimensions.X / 4), 0.0f);
            //Shape._radius = Dimensions.Length();
			FixtureDef.shape = Shape;
			FixtureDef.filter.groupIndex = (short)CollisionGroup;
			FixtureDef.density = 1;
			FixtureDef.friction = Friction;
            
			_physicsBody.CreateFixture(FixtureDef);

            FootShape._radius = Dimensions.X / 2;
            FootShape._p = new Vector2(0, -Dimensions.Y / 2 + Dimensions.X / 2);
            FixtureDef.shape = FootShape;
            _physicsBody.CreateFixture(FixtureDef);

			_physicsBody.SetUserData(this);

            MassData.mass = Mass;
			MassData.center = Vector2.Zero;
			MassData.I = MassData.mass * MassData.mass / 12;
			if (PreventRotation) MassData.I = 0;
			_physicsBody.SetMassData(ref MassData);

            //Set initial rotation
            _physicsBody.Rotation = Me.Angle;

			base._Initialize(Content);
		}

        public struct ContactImpulse
        {
            public Vector2 Normal;
            public Vector2 Impulse;
        }

        [ContentSerializerIgnore]
        public List<ContactImpulse> ContactImpulses = new List<ContactImpulse>();

        [ContentSerializerIgnore]
        public float CrushForce = 0.0f;

        [ContentSerializer(Optional = true)]
        public float CrushThreshold = 512.0f;

        internal void ProcessContacts()
        {
            IsOnGround = false;
            CrushForce = 0.0f;
            Vector2 GroundNormalAccum = Vector2.Zero;
            int GroundVectors = 0;

            for (int A = 0; A < ContactImpulses.Count; ++A)
            {
                if (Vector2.Dot(Jemgine.Math.Vector.Normalize(Gravity), ContactImpulses[A].Normal) > 0.33f)
                {
                    GroundNormalAccum += -ContactImpulses[A].Normal;
                    GroundVectors += 1;
                }

                for (int B = A + 1; B < ContactImpulses.Count; ++B)
                {
                    float Angle = System.Math.Abs(Jemgine.Math.Vector.AngleBetweenVectors(
                        ContactImpulses[A].Normal, ContactImpulses[B].Normal));
                    if (Angle > System.Math.PI * 0.75f)
                    {
                        float Force = ContactImpulses[A].Impulse.Length() + ContactImpulses[B].Impulse.Length();
                        if (Force > CrushForce) CrushForce = Force;
                    }
                }
            }

            if (GroundVectors > 0)
            {
                GroundNormal = GroundNormalAccum / GroundVectors;
                IsOnGround = true;
                TimeSinceOnGround = 0;
            }
            else
            {
                TimeSinceOnGround += 1;
            }

            ContactImpulses.Clear();

        }
        		
        public override void _Destroy()
        {
            Me.Sim.GetModule<PhysicsModule>().Actors.Remove(this);
            base._Destroy();
        }

	}
}
